#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cerrno>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "log.hpp"
#include "protocol.hpp"

using namespace std;

namespace Client
{
    static const int g_num = 1024;

    enum
    {
        USAGE_ERROR = 1,
        SOCKET_ERROR,
        CONNECT_ERROR
    };
    class calClient
    {
    public:
        calClient(const string &serverIP, const uint16_t serverPort) : _sockfd(-1), _serverIP(serverIP), _serverPort(serverPort)
        {
        }
        ~calClient()
        {
            if (_sockfd >= 0)
                close(_sockfd); // 写不写皆可，因为进程打开的文件的生命周期是跟随进程的
        }

        void init()
        {
            // 1. 创建套接字
            _sockfd = socket(AF_INET, SOCK_STREAM, 0); // tcp通信
            if (_sockfd == -1)
            {
                // logMessage(FATAL, "create socket error");// 客户端不再打印日志
                cerr << "create socket error: " << errno << " : " << strerror(errno) << endl;
                exit(SOCKET_ERROR);
            }
            logMessage(NORMAL, "create socket success: %d", _sockfd); // 测试

            // 2. 绑定——tcp客户端需要bind，但不需要显式bind

            // 3. tcp客户端需要listen吗？——不需要

            // 4. 客户端需要accept吗？——不需要

            // 5. 客户端需要做什么？——因为tcp通信是面向连接的，所以，tcp客户端要向服务器发起连接
        }
        void run()
        {
            // 5. 发起连接
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));

            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr(_serverIP.c_str());
            server.sin_port = htons(_serverPort);

            if (connect(_sockfd, (struct sockaddr *)&server, sizeof(server)) != 0)
            {
                cerr << "connect error: " << errno << " : " << strerror(errno) << endl;
                exit(CONNECT_ERROR);
            }

            // 开始通信
            string line;
            char buffer[g_num];

            std::string inBuffer;
            while (true)
            {
                cout << "myCal> ";
                getline(cin, line); // 如：1 + 1

                // 发送请求
                Request req = ParseLine(line); // 定义请求 如 "1+1"
                std::string content;
                req.serialize(&content);
                std::string send_string = enLength(content);               // 添加协议报头
                send(_sockfd, send_string.c_str(), send_string.size(), 0); // 发送 // 有bug

                // 读取响应
                std::string package;
                if (!recvPackage(_sockfd, inBuffer, &package))
                    continue;
                // 走到这里，一定读取到了至少一个完整的回送报文
                std::string resp_content;
                if (!deLength(package, &resp_content))
                    continue;
                // "exitcode result"
                Response resp;
                resp.deserialize(resp_content);
                std::cout << "exitcode: " << resp._exitcode << " result: " << resp._result << std::endl;
            }
        }

    private:
        Request ParseLine(const std::string &line)
        {
            // line的情况： "1+1" "11+12"
            int status = 0; // 0:操作符之前  1: 操作符  2: 操作符之后
            int cnt = line.size();
            std::string left, right;// 操作数
            char op;// 运算
            int i = 0;
            while (i < cnt)
            {
                switch (status)
                {
                case 0:
                    if(!isdigit(line[i]))
                    {
                        op = line[i++];
                        status = 1;
                    }
                    else left.push_back(line[i++]);
                    break;
                case 1:
                    status = 2;
                    break;
                case 2:
                    right.push_back(line[i++]);
                    break;
                default:
                    break;
                }
            }
            // std::cout << std::stoi(left) << " " << std::stoi(right) << " " << op << std::endl;
            return Request(std::stoi(left), std::stoi(right), op);
        }

    private:
        string _serverIP;
        uint16_t _serverPort;
        int _sockfd;
    };
}